package com.ruoyi.common.utils.http;

import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.JSONReader;
import com.alibaba.fastjson2.TypeReference;
import com.ruoyi.common.exception.BigDataException;
import com.ruoyi.common.exception.HttpException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.SimpleClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import java.lang.reflect.Type;
import java.net.SocketTimeoutException;
import java.util.Arrays;
import java.util.Map;

/**
 * 描述 HTTP请求工具类
 * 作者 ZhangPing
 * 创建时间 2023/8/16 17:16
 */
@Slf4j
public class HttpRequest {

    private static final RestTemplate restTemplate = restTemplate();

    /**
     * 需要忽略日志的Url
     */
    private static final String[] logIgnoreUrls = {
            "/gpsData/queryAllLastGps",
            "/gpsData/queryListLastGps",
            "/batch/card/info"
    };

    /**
     * 请求超时设置
     */
    public static RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(10000);
        factory.setReadTimeout(10000);
        return new RestTemplate(factory);
    }

    /**
     * POST请求
     *
     * @param url
     * @param paramMap
     * @return BigDataResponse
     */
    public static <T> BigDataResponse<T> postJsonBigData(String url, JSONObject paramMap, Type responseType) throws RestClientException {
        try {
            JSONObject resJson = sendPost(url, paramMap);
            logRequestResult(url, resJson);

            // 使用TypeReference解析嵌套泛型
            Type pageDataType = new TypeReference<BigDataResponse.PageData<T>>() {}.getType();
            BigDataResponse.PageData<T> pageData = resJson.getObject(
                    "data",
                    pageDataType,
                    JSONReader.Feature.SupportSmartMatch
            );

            // 构造响应对象（不再传入totalCount顶层参数）
            return new BigDataResponse<>(
                    resJson.getIntValue("code"),
                    resJson.getString("msg"),
                    pageData
            );

//            return new BigDataResponse<>(resJson.getIntValue("code"), resJson.getString("msg"),
//                    resJson.getObject("detail", responseType, JSONReader.Feature.SupportSmartMatch), resJson.getInteger("totalCount"));
        } catch (Exception e) {
            e.printStackTrace();
            log.debug("请求异常：" + e.getMessage());
            if (e instanceof SocketTimeoutException || e instanceof ResourceAccessException){
                throw new BigDataException("请求超时");
            }else {
                throw new BigDataException(e.getMessage());
            }
        }
    }

    public static <T> CommonResponse<T> postJson(String url, JSONObject paramMap, Type responseType) throws RestClientException {
        try {
            JSONObject resJson = sendPost(url, paramMap);
            logRequestResult(url, resJson);

            return new CommonResponse<>(resJson.getIntValue("code"), resJson.getString("msg"),
                    resJson.getObject("data", responseType, JSONReader.Feature.SupportSmartMatch));
        } catch (Exception e) {
            log.debug("请求异常：" + e.getMessage());
            if (e instanceof SocketTimeoutException || e instanceof ResourceAccessException){
                throw new HttpException("请求超时");
            }else {
                throw new HttpException(e.getMessage());
            }
        }
    }

    public static <T> CommonResponse<T> postUrl(String url, JSONObject paramMap, Type responseType) throws RestClientException {
        try {
            JSONObject resJson = sendPostUrl(url, paramMap);
            logRequestResult(url, resJson);

            return new CommonResponse<>(resJson.getIntValue("code"), resJson.getString("msg"),
                    resJson.getObject("data", responseType, JSONReader.Feature.SupportSmartMatch));

        } catch (Exception e) {
            log.debug("请求异常：" + e.getMessage());
            if (e instanceof SocketTimeoutException || e instanceof ResourceAccessException){
                throw new HttpException("请求超时");
            }else {
                throw new HttpException(e.getMessage());
            }
        }
    }

    /**
     * GET请求
     *
     * @param url
     * @param paramMap
     * @return BigDataResponse
     */
    public static <T> BigDataResponse<T> getBigData(String url, JSONObject paramMap, Type responseType) {
        try {
            JSONObject resJson = sendGet(url, paramMap);
            logRequestResult(url, resJson);

            // 统一使用data字段解析
            Type pageDataType = new TypeReference<BigDataResponse.PageData<T>>() {}.getType();
            BigDataResponse.PageData<T> pageData = resJson.getObject(
                    "data",
                    pageDataType,
                    JSONReader.Feature.SupportSmartMatch
            );

            return new BigDataResponse<>(
                    resJson.getIntValue("code"),
                    resJson.getString("msg"),
                    pageData
            );

//            return new BigDataResponse<>(resJson.getIntValue("code"), resJson.getString("msg"),
//                    resJson.getObject("detail", responseType, JSONReader.Feature.SupportSmartMatch), resJson.getInteger("totalCount"));


        } catch (Exception e) {
            log.debug("请求异常：" + e.getMessage());
            if (e instanceof SocketTimeoutException || e instanceof ResourceAccessException){
                throw new BigDataException("请求超时");
            }else {
                throw new BigDataException(e.getMessage());
            }
        }
    }

    /**
     * GET请求
     *
     * @param url
     * @param paramMap
     * @return BigDataResponse
     */
    public static <T> BigDataResponse<T> getBigDataNoEncode(String url, JSONObject paramMap, Type responseType) {
        try {
            JSONObject resJson = sendGetNoEncode(url, paramMap);
            logRequestResult(url, resJson);

            Type pageDataType = new TypeReference<BigDataResponse.PageData<T>>() {}.getType();
            BigDataResponse.PageData<T> pageData = resJson.getObject(
                    "data",
                    pageDataType,
                    JSONReader.Feature.SupportSmartMatch
            );

            return new BigDataResponse<>(
                    resJson.getIntValue("code"),
                    resJson.getString("msg"),
                    pageData
            );

//            return new BigDataResponse<>(resJson.getIntValue("code"), resJson.getString("msg"),
//                    resJson.getObject("detail", responseType, JSONReader.Feature.SupportSmartMatch), resJson.getInteger("totalCount"));


        } catch (Exception e) {
            log.debug("请求异常：" + e.getMessage());
            if (e instanceof SocketTimeoutException || e instanceof ResourceAccessException){
                throw new BigDataException("请求超时");
            }else {
                throw new BigDataException(e.getMessage());
            }
        }
    }

    public static <T> CommonResponse<T> get(String url, JSONObject paramMap, Type responseType) {
        try {
            JSONObject resJson = sendGet(url, paramMap);
            logRequestResult(url, resJson);
            return new CommonResponse<>(resJson.getIntValue("code"), resJson.getString("msg"),
                    resJson.getObject("data", responseType, JSONReader.Feature.SupportSmartMatch));
        } catch (Exception e) {
            log.debug("请求异常：" + e.getMessage());
            if (e instanceof SocketTimeoutException || e instanceof ResourceAccessException){
                throw new HttpException("请求超时");
            }else {
                throw new HttpException(e.getMessage());
            }
        }
    }


    private static JSONObject sendPost(String url, JSONObject paramMap) throws Exception {
        log.debug("发送HTTP请求");
        log.debug("POST URL：" + url);
//        log.debug("参数：" + (paramMap == null ? "无" : paramMap.toJSONString()));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<Map<String, Object>> request = new HttpEntity<>(paramMap, headers);
        return restTemplate.postForObject(url, request, JSONObject.class);
    }

    private static JSONObject sendPostUrl(String url, JSONObject paramMap) throws Exception {
        log.debug("发送HTTP请求");
        log.debug("POST URL：" + url);
//        log.debug("参数：" + (paramMap == null ? "无" : paramMap));
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        //普通Map转MultiValueMap
        MultiValueMap<String, Object> multiValueMap = new LinkedMultiValueMap<>();
        paramMap.forEach(multiValueMap::add);
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity<>(multiValueMap, headers);
        return restTemplate.postForObject(url, request, JSONObject.class);
    }

    private static JSONObject sendGet(String url, JSONObject paramMap) throws Exception {
        //URL拼接参数
        UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url);
        if (paramMap != null){
            for (String key : paramMap.keySet()) {
                builder.queryParam(key, paramMap.get(key));
            }
        }
        url = builder.toUriString();
        log.debug("发送HTTP请求");
        log.debug("GET URL：" + url);
        return restTemplate.getForObject(url, JSONObject.class);
    }

    private static JSONObject sendGetNoEncode(String url, JSONObject paramMap) throws Exception {
        //URL拼接参数
        StringBuilder builder = new StringBuilder(url + "?");
        if (paramMap != null){
            for (String key : paramMap.keySet()) {
                builder.append(key).append("=").append(paramMap.get(key)).append("&");
            }
        }
        url = builder.substring(0, builder.length() - 1);
        log.debug("发送HTTP请求");
        log.debug("GET URL：" + url);
        return restTemplate.getForObject(url, JSONObject.class);
    }

    /**
     * 打印请求结果
     * @param url
     * @return
     */
    private static void logRequestResult(String url, JSONObject resJson){
        long count = Arrays.asList(logIgnoreUrls).stream().filter(url::contains).count();
        if (count > 0){
            //不打印请求结果
            log.debug("请求成功");
        }else {
            //log.debug("请求结果：" + resJson.toJSONString());
            log.debug("请求结果：" + count);
        }
    }
}
